package com.arthur.common.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.ServletRequest;

import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;



import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;



public class DynamicSpecifications {
	private static final Logger logger = LoggerFactory.getLogger(DynamicSpecifications.class);
	
	/**
	 * 动态查询，参数前缀
	 */
	public final static String SEARCH_PREFIX = "search_";
	
	private static final String SHORT_DATE = "yyyy-MM-dd";
	private static final String LONG_DATE = "yyyy-MM-dd mm:HH:ss";
	private static final String TIME = "mm:HH:ss";
	
	
	public static Example bySearchFilter(Map<String, Object> searchParams, final Class entityClazz) {
		Map<String, SearchFilter> searchFilters = SearchFilter.parse(searchParams);
		return toExample(searchFilters.values(),entityClazz);
	}
	
	public static Example bySearchFilter(ServletRequest request, final Class entityClazz) {
		Map<String, Object> searchParams = getParametersStartingWith(request,SEARCH_PREFIX);
		Map<String, SearchFilter> searchFilters = SearchFilter.parse(searchParams);
		return toExample(searchFilters.values(),entityClazz);
	}
	
	public static Example bySearchFilter(ServletRequest request,final Class entityClazz,final Object object) {
		Map<String, Object> searchParams = getParametersStartingWith(request,SEARCH_PREFIX);
		Map<String, Object> searchObjectParams = getParametersByObject(object);
		searchParams.putAll(searchObjectParams);
		Map<String, SearchFilter> searchFilters = SearchFilter.parse(searchParams);
		Example requestExample = toExample(searchFilters.values(),object.getClass());
		return requestExample;
	}
	
	
	
	/**
	 * 取得带相同前缀的Request Parameters, copy from spring WebUtils.
	 * 
	 * 返回的结果的Parameter名已去除前缀.
	 */
	@SuppressWarnings("rawtypes")
	public static Map<String, Object> getParametersStartingWith(ServletRequest request, String prefix) {
		Validate.notNull(request, "Request must not be null");
		Enumeration paramNames = request.getParameterNames();
		Map<String, Object> params = new TreeMap<String, Object>();
		if (prefix == null) {
			prefix = "";
		}
		while ((paramNames != null) && paramNames.hasMoreElements()) {
			String paramName = (String) paramNames.nextElement();
			if ("".equals(prefix) || paramName.startsWith(prefix)) {
				String unprefixed = paramName.substring(prefix.length());
				String[] values = request.getParameterValues(paramName);
				if ((values == null) || (values.length == 0)) {
					// Do nothing, no values found at all.
				} else if (values.length > 1) {
					params.put(unprefixed, values);
				} else {
					params.put(unprefixed, values[0]);
				}
			}
		}
		return params;
	}
	
	public static Map<String, Object> getParametersByObject(Object object) {
		Map<String, Object> params = new TreeMap<String, Object>();
		Field[] fields = object.getClass().getDeclaredFields();
		for (Field field : fields) {
			String getMethodName = StringUtils.capitalize(field.getName());
			Object value = null;
			try {
				Method method = object.getClass().getDeclaredMethod("get"+getMethodName, null);
				value = method.invoke(object, null);
			} catch (Exception e) {
			}
			
			if(value!=null){
				params.put("EQ_"+field.getName(), value);
			}
		}
		
		return params;
	}
	public static Example toExample(final Collection<SearchFilter> searchFilters,final Class entityClazz){
		Example example = new Example(entityClazz);
		Criteria criteria = example.createCriteria();
		
		for (SearchFilter filter : searchFilters) {
			// logic operator
			switch (filter.getOperator()) {
			case EQ:
				criteria.andEqualTo(filter.getFieldName(), filter.getValue());
				break;
			case LIKE:
				criteria.andLike(filter.getFieldName(), "%" + filter.getValue() + "%");
				break;
			case GT:
				criteria.andGreaterThan(filter.getFieldName(), filter.getValue());
				break;
			case LT:
				criteria.andLessThan(filter.getFieldName(), filter.getValue());
				break;
			case GTE:
				criteria.andGreaterThanOrEqualTo(filter.getFieldName(), filter.getValue());
				break;
			case LTE:
				criteria.andLessThanOrEqualTo(filter.getFieldName(), filter.getValue());
				break;
			case IN:
				String[] values = StringUtils.split(filter.getValue().toString(),',');
				List<String> list = Arrays.asList(values);
				criteria.andIn(filter.getFieldName(), list);
				break;
			}
		}
		return example;
	}
	
	
	private static Date convert2Date(String dateString) {
		SimpleDateFormat sFormat = new SimpleDateFormat(SHORT_DATE);
		try {
			return sFormat.parse(dateString);
		} catch (ParseException e) {
			try {
				return sFormat.parse(LONG_DATE);
			} catch (ParseException e1) {
				try {
					return sFormat.parse(TIME);
				} catch (ParseException e2) {
					logger.error("Convert time is error! The dateString is " + dateString + "." + Exceptions.getStackTraceAsString(e2));
				}
			}
		}

		return null;
	}
		
	
	private static <E extends Enum<E>> E convert2Enum(Class<E> enumClass, String enumString) {
		return EnumUtils.getEnum(enumClass, enumString);
	}
	
}
